import java.util.ArrayList;
import java.lang.Math;

import static java.lang.System.out;

public final class QABMiniMax extends FixedDepth {
    public QABMiniMax(boolean isBlack, Eval evaluator, int searchDepth) {
        super(isBlack, evaluator, searchDepth);
    }

    @Override
    public Board findMove(Board board) {
        // IMPORTANT! If the player using findMove is White, then
        // the search method will be called with BLACK to move and
        // vice versa, i.e. THE RETURN VALUE FROM THE SEARCH-CALL WILL
        // REFLECT THE OPPONENTS BEST POSSIBLE SCORE
        assert board.blackToMove == isBlack;

        int bestValue = MIN;
        if (isBlack) {
            bestValue = MAX;
        }
	int alpha = MIN;
	int beta = MAX;


        ArrayList<Board> moves = board.generateMoves();

	// If no moves found, return null as error flag
	if( moves.isEmpty() ) {
	    return null;
	}
	
	// If only one possible move found - just return that.
	if( moves.size() == 1 ) {
	    return moves.get(0);
	}


        Board bestMove = moves.get(0);
        for (Board move : moves) {
            int value = search(move, searchDepth, alpha, beta);
            if (isBlack) {
                if (value < bestValue) {
		    if (value < beta) {
			beta = value;
		    }
                    bestValue = value;
                    bestMove = move;
                }
            } else {
                if (value > bestValue) {
		    if (value > alpha) {
			alpha = value;
		    }
                    bestValue = value;
                    bestMove = move;
                }
            }
        }

        if (bestMove != null && printBestMoveValue) {
            out.println("The best move has value " + bestValue);
        }

        return bestMove;
    }


    public int search(Board board, int depthLeft, int alpha, int beta) {

	Boolean canJump = true;
        ArrayList<Board> moves = new ArrayList<Board>();
	board.generateJumps(moves);
	if(moves.size() == 0) {
	    board.generateSteps(moves);
	    canJump = false;
	}


        //if unable to move, this is a losing board for current player
	//punish for depth left (embarrassing to lose with much depth left)
        if(moves.isEmpty()) {
            if (board.blackToMove) {
                return BLACK_LOSS+depthLeft;
            } else {
                return WHITE_LOSS-depthLeft;
            }
        }

	if (depthLeft == 0) {
	    if (canJump) {
		depthLeft++;
	    }
	    else {
		return evaluator.eval(board);
	    }
        }
	

	depthLeft--;

	int nodeCount = 0;
        while (  (nodeCount < moves.size())  &&  (alpha < beta)  ) {
	    Board move = moves.get(nodeCount);
            int value = search(move, depthLeft, alpha, beta);
            if (board.blackToMove) {
                if (value < beta) {
                    beta = value;
                }
            } else {
                if (value > alpha) {
                    alpha = value;
                }
            }
	    nodeCount++;
        }

	int bestValue;
        if (board.blackToMove) {
            bestValue = beta;
        } else {
            bestValue = alpha;
        }

        return bestValue;
    }
}
